#define NOMINMAX
#include "AppMenuPanel.h"

#include <core/FileSys.h>
#include <core/ImageMgr.h>
#include <core/StyleMgr.h>
#include <core/Utility.h>

#include <QBoxLayout>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QMouseEvent>
#include <QPainterPath>
#include <QStylePainter>
#include <QTimerEvent>
#include <chrono>

#include "../../IDefine.h"
#include "../CMD/CmdMgr.h"
#include "AppMenuCategoryPanel.h"
#include "AppMenuCommentsPanel.h"

using namespace std::chrono;

// TODO from configure
static constexpr int s_first_menu_height = /*40*/ 50;
static constexpr int s_first_menu_width = /*123*/ 150;
static constexpr int s_first_menu_space = 5;
static constexpr int s_first_menu_left_space = 9;
static constexpr int s_menu_logoHeight = 55;
static constexpr int s_max_favs = 9;

static system_clock::time_point s_timer_start = system_clock::time_point::max();
static QMap<int, system_clock::time_point> s_hover_times;

AppMenuPanel* AppMenuPanel::s_menu = nullptr;

static QRgb blendColorByTime(QRgb c1, QRgb c2, int elapsed, int max = 500) {
  if (elapsed <= 0) return c1;
  if (elapsed > max) return c2;
  uint8_t alpha = (uint8_t)(elapsed * 255.0 / max);
  alpha = alpha <= 40 ? 40 : alpha;

  if (alpha == 255)
    return c2;
  else if (alpha == 0)
    return c1;

  int r = qRed(c1) + (((qRed(c2) - qRed(c1)) * alpha) >> 8);
  int g = qGreen(c1) + (((qGreen(c2) - qGreen(c1)) * alpha) >> 8);
  int b = qBlue(c1) + (((qBlue(c2) - qBlue(c1)) * alpha) >> 8);
  return qRgb(r, g, b);
}

AppMenuPanel::AppMenuPanel(QWidget* parent)
    : QFrame(parent), m_imgLogo(nullptr) {
  m_imgLogo = ImgMgr::instance().image(
      "guiframe_app_logo_title", WidgetStatus::kNormal, devicePixelRatioF());

  setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);

  // use the stylesheet to draw border, will cause the panel pos changed when
  // show comments panel!! setStyleSheet("AppMenuPanel {border: 1px solid green;
  // }"); setStyleSheet("border: 1px solid green;");
  setMouseTracking(true);
  setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

  s_menu = this;
  construct();
  loadMenuComments();  // do it in a thread?
}

void AppMenuPanel::onItemHoverChanged(int id, bool hovered) {
  if (!hovered && m_commentsPanel->isVisible()) {
    int width = size().width() - 228;
    m_commentsPanel->setVisible(false);
    setFixedWidth(width);
    resize(width, size().height());
  } else if (hovered && m_menuComments.contains(id)) {
    int width = size().width();
    m_commentsPanel->setComments(m_menuComments[id]);
    m_commentsPanel->resize(228, size().height());
    bool isVisibleBefore = m_commentsPanel->isVisible();
    m_commentsPanel->setVisible(true);
    if (!isVisibleBefore) {
      setFixedWidth(width + 228);
      resize(width + 228, size().height());
    }
  }
}

void AppMenuPanel::onItemAction(int id, bool addToFav) {
  if (m_FavPanel) {
    if (addToFav) {
      m_FavPanel->addCmd(id);
      CmdMgr::instance().addCommonPage(id);
    } else {
      m_FavPanel->removeCmd(id);
      CmdMgr::instance().removeCommonPage(id);
    }
  }
}

void AppMenuPanel::setFavPanel(AppMenuCategoryPanel* panel) {
  m_FavPanel = panel;
  if (m_FavPanel) {
    for (auto it : CmdMgr::instance().commonPages()) {
      m_FavPanel->addCmd(it);
    }
  }
}

bool AppMenuPanel::canAddToFav() const {
  if (m_FavPanel) return m_FavPanel->cmdCount() < s_max_favs;

  return false;
}

bool AppMenuPanel::hasAddedToFav(int cmdId) const {
  if (m_FavPanel) return m_FavPanel->hasCmd(cmdId);
  return false;
}

void AppMenuPanel::paintEvent(QPaintEvent* event) {
  QStylePainter painter(this);
  QRect r = rect();

  // 绘制背景色
  painter.fillRect(r, NAME_COLOR("MenuTitleBack"));

  // 绘制边框线
  QPen pen;
  pen.setWidth(1);
  pen.setColor(NAME_COLOR("Hover"));
  painter.setPen(pen);
  painter.setBrush(Qt::NoBrush);
  painter.drawRect(r);

  // 绘制菜单logo、name、title
  QRect rc(s_first_menu_left_space, s_first_menu_left_space, s_first_menu_width,
           s_menu_logoHeight);
  int top = rc.top() - 6;
  rc.setRect(rc.left(), top, m_imgLogo->width(), m_imgLogo->height());
  painter.setRenderHint(QPainter::SmoothPixmapTransform);
  painter.drawImage(rc, *m_imgLogo);
  rc.setRect(rc.right() + 7, top, m_imgLogo->width(), m_imgLogo->height());
  QFont font = painter.font();
  font.setPixelSize(12);
  painter.setFont(font);

  // 绘制菜单左侧分类
  for (int i = 0; i < m_firstLabels.size(); i++) {
    QRect rect = m_rects[i];
    if (m_activeMenu == i) {
      painter.fillRect(rect, NAME_COLOR("MenuTitleHover"));
    } else {
      if (i == m_hoverMenu) {
        painter.fillRect(rect, NAME_COLOR("MenuTitleHover"));
      } else if (m_timerAnimate == -1) {
        // painter.fillRect(rect, /*QColor(23, 36,
        // 34)*/NAME_COLOR("MainTitleBack"));
      } else {
        // 鼠标滑动时，颜色淡化至背景色
        QColor c1(25, 61, 55);
        QColor c2 = NAME_COLOR("MenuTitleBack");
        QColor c(blendColorByTime(
            c1.rgba(), c2.rgba(),
            duration_cast<milliseconds>(system_clock::now() - s_hover_times[i])
                .count()));
        painter.fillRect(rect, c);
      }
    }

    rect.adjust(10, 0, 0, 0);
    font.setPixelSize(15);
    font.setBold(true);
    painter.setFont(font);
    painter.setPen(QPen(NAME_COLOR("Highlight")));
    painter.drawText(rect, Qt::AlignVCenter, m_firstLabels[i].second);

    if (i < m_redLabel.size() && m_redLabel[i].second) {
      int fTxtWidth =
          QFontMetrics(font).horizontalAdvance(m_firstLabels[i].second);
      int fTxtHeight = QFontMetrics(font).height();
      painter.setPen(QColor(0xDF201F));
      painter.setBrush(QColor(0xDF201F));
      QPointF fPoint(rect.left() + fTxtWidth + 5,
                     rect.top() + (rect.height() - fTxtHeight) / 2);
      painter.drawEllipse(fPoint, 2.0f, 2.0f);
    }
  }
}

void AppMenuPanel::mouseMoveEvent(QMouseEvent* e) {
  // check which menu is the mouse on
  int hoverMenu = -1;
  QPoint pos = e->pos();
  for (int i = 0; i < m_rects.size(); i++) {
    if (m_rects[i].contains(pos)) {
      hoverMenu = i;
      break;
    }
  }
  if (hoverMenu >= 0 && hoverMenu < m_redLabel.size()) {
    if (m_redLabel[hoverMenu].second) {
      m_redLabel[hoverMenu].second = false;
      if (hoverMenu < m_categories.size()) {
        QVector<AppMenuCategoryPanel*> panels = m_categories[hoverMenu];
        for (AppMenuCategoryPanel* panel : panels) {
          panel->removeRedPoint();
        }
      }
    }
  }

  // start timer to wait a while, if the mouse is still on it, switch menu
  if (hoverMenu != m_hoverMenu) {
    killSwitchTimer();
    if (m_hoverMenu != -1) s_hover_times[m_hoverMenu] = system_clock::now();
    m_hoverMenu = hoverMenu;
    if (m_hoverMenu != -1) m_timerSwitch = startTimer(500);
    if (m_timerAnimate == -1) {
      m_timerAnimate = startTimer(40);
      s_timer_start = system_clock::now();
    }
    update();
  }
}

void AppMenuPanel::timerEvent(QTimerEvent* e) {
  if (e->timerId() == m_timerSwitch) {
    killAnimateTimer();
    killSwitchTimer();
    switchMenu(m_hoverMenu);
  } else if (e->timerId() == m_timerAnimate) {
    auto elapsed =
        duration_cast<milliseconds>(system_clock::now() - s_timer_start);
    if (elapsed.count() > 1000)  // 1second
    {
      killAnimateTimer();
    }
    update();
  }
}

void AppMenuPanel::loadMenuComments() {
  QJsonDocument doc = CoreUtility::openJsonDoc(
      FileSys::instance()->appCfgDir() + "/menuComment.json");
  if (doc.isNull()) {
    logError(MODULE_NAME, "Open qb menuComment config file failed!");
    return;
  }
  QJsonArray comments = doc.array();

  QJsonValue comment;
  foreach (comment, comments) {
    QJsonObject obj = comment.toObject();
    int id = obj["id"].toInt();

    MenuComments mc;
    mc.title = obj["title"].toString();

    QJsonArray commentList = obj["comment"].toArray();
    QJsonValue var;
    foreach (var, commentList) {
      QJsonObject varObj = var.toObject();
      QString text = varObj["text"].toString();
      QString colorStr = varObj["color"].toString();
      QColor color(colorStr.toInt(nullptr, 16));
      mc.comments.push_back({text, color});
    }

    m_menuComments[id] = mc;
  }
}

void AppMenuPanel::onCommonPage(int* ids, int size, int ntype) {
  // do commonpage
  switch (ntype) {
    case 0:
      CmdMgr::instance().setCommonPages(ids, size);
      break;
    default:
      break;
  }
}

void AppMenuPanel::construct() {
  if (m_mainLayout) {
    switchMenu(-1);
  } else {
    m_mainLayout = new QHBoxLayout();
    m_mainLayout->setContentsMargins(0, 0, 0, 0);
    m_mainLayout->setSpacing(0);
    m_mainLayout->addSpacing(s_first_menu_width + s_first_menu_left_space + 10);
  }

  QJsonDocument doc =
      CoreUtility::openJsonDoc(FileSys::instance()->appCfgDir() + "/menu.json");
  if (doc.isNull()) {
    logError(MODULE_NAME, "Open menu configure failed!");
    return;
  }

  int x = s_first_menu_left_space + 1;  // 1:border
  int y = s_first_menu_space + s_menu_logoHeight;

  int index = 0;
  QJsonArray menuObj = doc.array();
  if (m_categories.size() > 0) {
    m_categories.clear();
  }
  m_rects.clear();
  m_categories.resize(menuObj.size());

  if (m_firstLabels.size() > 0) {
    m_firstLabels.clear();
  }

  QJsonValue var;
  foreach (var, menuObj) {
    // first level
    QJsonObject obj = var.toObject();
    // second level:
    QJsonArray menuCateogries = obj["submenu"].toArray();
    for (QJsonValue categoryObj : menuCateogries) {
      QJsonObject obj = categoryObj.toObject();
      AppMenuCategoryPanel* categoryPanel =
          new AppMenuCategoryPanel(&obj, this);
      if (categoryPanel->cmdCount() == 0 && !categoryPanel->isFavePanel()) {
        categoryPanel->deleteLater();
        continue;
      }
      categoryPanel->setVisible(false);
      m_categories[index].push_back(categoryPanel);
    }
    if (m_categories[index].size() == 0) {
      continue;
    }

    int id = obj["id"].toInt();
    QString title = obj["title"].toString();
    m_firstLabels.push_back({id, title});

    QRect rect(x, y, s_first_menu_width, s_first_menu_height);
    m_rects.push_back(rect);
    y += s_first_menu_height;

    s_hover_times[index] = system_clock::now();
    ++index;
  }

  if (!m_commentsPanel) {
    m_commentsPanel = new AppMenuCommentsPanel(this);
    m_commentsPanel->setVisible(false);
  }

  setLayout(m_mainLayout);
  initRedPointLabel();
  switchMenu(0);
}

void AppMenuPanel::switchMenu(int index) {
  // remove old widgets and set unvisible
  if (m_commentsPanel) {
    m_commentsPanel->setVisible(false);
  }
  if (m_activeMenu != -1) {
    QVector<AppMenuCategoryPanel*> panels = m_categories[m_activeMenu];
    for (AppMenuCategoryPanel* panel : panels) {
      panel->setVisible(false);
      m_mainLayout->removeWidget(panel);
    }
  }
  m_mainLayout->removeWidget(m_commentsPanel);

  m_activeMenu = index;

  int menuWidth = s_first_menu_width + s_first_menu_left_space + 10;
  if (m_activeMenu != -1) {
    QVector<AppMenuCategoryPanel*> panels = m_categories[m_activeMenu];
    for (AppMenuCategoryPanel* panel : panels) {
      panel->setVisible(true);
      m_mainLayout->addWidget(panel);
    }
    menuWidth += 170 * panels.size();
  }

  m_mainLayout->addWidget(m_commentsPanel);
  setFixedWidth(menuWidth);
  resize(menuWidth, std::max(m_rects.last().bottom() + 10,
                             m_mainLayout->sizeHint().height()));

  update();
}

void AppMenuPanel::initRedPointLabel() {
  m_redLabel.clear();

  int nIndex = 0;
  for (auto it : m_categories) {
    bool bRedPoint = false;
    for (auto itSub : it) {
      if (itSub && itSub->isRedPoint()) {
        bRedPoint = true;
        break;
      }
    }
    m_redLabel.push_back({nIndex, bRedPoint});
    ++nIndex;
  }
}

void AppMenuPanel::killAnimateTimer() {
  if (m_timerAnimate == -1) return;

  killTimer(m_timerAnimate);
  m_timerAnimate = -1;
}

void AppMenuPanel::killSwitchTimer() {
  if (m_timerSwitch == -1) return;

  killTimer(m_timerSwitch);
  m_timerSwitch = -1;
}
